LEDフリーラン点滅

タイマ割り込み(Dynamic Timer Drivers Library)を使った場合
                         (→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード


   LEDのフリーラン点滅を タイマ割り込み(Dynamic Timer Drivers Library)を使って行う場合の例を以下に説明します。
   
<仕様>
 ・ PIC32MZを使用する
 ・ LEDは1sec ON、1sec OFFを繰り返す
 ・ 動作クロックは外部に24MHzの水晶発振器を接続しこれを使用する    
 ・ 時間制御は、割り込みを使い Harmonyのダイナミックタイマドライバー ライブラリを使うこと。
 
<回路図>(→回路図のPDFファイル


 


<外観>
<動作結果>
(画像をクリックすると プログラム動作中の動画がYouTubeで再生します)

     
ディスクリートキバン

(PIC32MZ QFPモジュール組立キット → 購入方法
(QFPモジュール用ユニバーサルキバン → 購入方法

 
PIC32MZ評価ボード(MZ100-A001)

( → 購入方法 )
動作結果の
   動画再生
    








  解説
   
 右の表は、MHCでダイナミックタイマドライバーライブラリー(以下、ダイナミックタイマと云う)を選択した場合に生成されるファイルの抜粋です。

ソースコードの黒字の部分はMHCが自動生成する部分です。緑字部分がLEDフリーラン(LED 1秒点灯、1秒消灯の繰り返し)として追加記入した部分です。


■ 基本的な使い方

ダイナミックタイマを使った場合の基本的方法は以下です。

(1) タイマハンドル取得
myHandle = DRV_TMR_Open ( );

(2) 周期レジスタ初期値セット
uint32_t divider =  .;

(3) コールバック関数繰り返し呼び出しをセットアップ
DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR );

(4) タイマ割り込み許可、タイマ1イネーブル
DRV_TMR_Start(myHandle);


 コールバック関数名はアプリケーション側で(Harmonyの関数とバティングしない範囲で)自由に命名できます。


 関数呼び出しフロー

 ダイナミックタイマに於ける関数呼び出しのフローは以下のようになっています。かなり複雑です。

(1) PICが起動するとmain.cのSYS_Initialize()からsystem_init.cのSYS_Initialize()が呼び出され初期化が始まります。

(2) 本例の場合はタイマ1への入力周波数(ペリフェラルクロック3) を10MHzにするために、PB3DIVレジスタの所要ビットをDIV3DIVbits.PBDIV = 0x13に変更します。 このレジスタ変更に際してはシステムロックが必要となります。

(3)関数DRV_TMR_Initialize(DRV_TMR_INDEX_0, (SYS_MODULE_INIT *)&drvTmr0InitData);でタイマ1が初期化されます。引数のDRV_TMR_INDEX_0(値:0)はドライバーのインデックス番号でMHCが自動的に割り当ててくれます。本例では(データシート上の)ハードウェアタイマ1がダイナミックタイマドライバーDRV_TMR_INDEX_0に割り当てられています。

(4)アドレスが引数となっているdrvTmr0InitDataダイナミックタイマの諸元を規定している構造体DRV_TMR_INITインスタンスで、system_init.cの中で初期化されています
 この初期化された値の内容が具体的に明示されているのはsystem_config.hの中です。


(5) SYS_Initialize()のDRV_TMR_Initialize( )drv_tmr.cのDRV_TMR_Initialize( )を呼び出します。

(6) drv_tmr.cのDRV_TMR_Initialize( )に於いて ダイナミックタイマの諸元の構造体DRV_TMR_INITのインスタンスtmrInitを生成して、この(ローカル)インスタンスに引数として取得したアドレスをコピーします。

(7)一方、タイマ用システムモジュールのディスクリプタのpTmrDcptインスタンスも生成し、ドライバーのインデックスが規定の範囲内にあるか、また既に初期化された同じインデックスがないかなどをチェックしています。


(8) また、タイマ用システムモジュールのインスタンスpTmrInstも生成し、それぞれの対応するメンバーにtmrInitのメンバーをコピーして初期化します。

(9) タイマ用システムモジュールのハンドルを戻り値として返します。 (この戻り値は タイマドライバーのハンドルではありません。タイマドライバーのハンドルはDRV_TMR_Open( )の戻り値です。)

(10) タイマ用システムモジュールのハンドルを取得します。これでタイマドライバーのハンドルを取得する準備ができました。

(11)app.cの中にあるAPP_Initialize( )のDRV_TMR_Open( )でタイマドライバーを初期化します。 引数はタイマ1が引き当てられているDRV_TMR_INDEX_0 と ドライバーが(マルチクライアントでない)単独クライアント用であることを明示する
DRV_IO_INTENT_EXCLUSIVE にします。

(12)tmr_drv.cのDRV_HANDLE DRV_TMR_Open( )を呼び出します。

(13)タイマ用システムモジュールが初期化確認などの所要事項の確認後、タイマードライバーのハンドルを戻り値として返します。

(14) タイマドライバーのハンドルmyHandleを取得します。

(15) DRV_TMR_AlarmRegister( )でコールバック関数の繰り返し呼び出しをセットアップします。

(16)DRV_TMR_AlarmRegiste( )を呼び出します。 この関数の第5引数
DRV_TMR_CALLBACK callBackは関数へのポインタで、drv_tmr.hの中で定義されています。


(17)DRV_TMR_Start( )でタイマの割り込みを許可し、イネーブル状態にします。

(18)タイマ割り込みが許可されるとsystem_interrupt.cの中にある割り込みベクトル関数__ISR(_TIMER_1_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0( )がタイマカウンタと周期レジスタ値が一致すると呼び出されます。
 そしてDRV_TMR_Tasks( );が呼び出されます。

(19)drv_tmr.cのDRV_TMR_Tasks( )が呼び出されます。

(20)次にDRV_TMR_Tasks()の中にある
DRV_TMR_ProcessEvents( )がよばれます。

(21)この関数の中で コールバック関数へのポインタDRV_TMR_CALLBACKなどををメンバーにもつDRV_TMR_CLIENT_OBJ構造体のポインタとしてpClientを宣言し、初期化します。

(22)コールバック関数の呼び出しが行われます。


(23) 以下、(18)〜(22)の繰り返しが行われます。
 
 ファイル名  ファイルの内容抜粋
 main.c  
.....
.....

int main ( void )
{
    
   SYS_Initialize ( NULL ); //システム初期化

    while ( true )
    {
        SYS_Tasks ( );      //繰り返し実行
    } 
    return ( EXIT_FAILURE );
}
  
 system_tasks.c  
.....
.....

void SYS_Tasks ( void )
{
....
....

    APP_Tasks();
}
  
 app.c
.....
.....


#include "app.h"

DRV_HANDLE  myHandle;
bool LED;

void tmrISR(uintptr_t context, uint32_t alarmCount ) //タイマコールバック関数
{    
    if(LED == false)
    {
        LED = true;
        PLIB_PORTS_PinSet( PORTS_ID_0, PORT_CHANNEL_G, 15 );    //RG15: LED点灯
        //LATGbits.LATG15 = 1;
        PLIB_TMR_Period16BitSet(TMR_ID_1,39063); 
                            //ISR内でないと周期レジスタの値は変えられない
                                //5 nsec x20 x 39063 x 256 = 1000.0128msec = 1sec 
    }
    else
    {
        LED = false; 
        PLIB_PORTS_PinClear( PORTS_ID_0, PORT_CHANNEL_G, 15 );  //RG15: LED消灯                
        //LATGbits.LATG15 = 0;
        PLIB_TMR_Period16BitSet(TMR_ID_1,39063);   //5sec  消灯 //x100
    } 
    
};

....
....

void APP_Initialize ( void )
{
  
    appData.state = APP_STATE_INIT;

    myHandle = DRV_TMR_Open ( DRV_TMR_INDEX_0, DRV_IO_INTENT_EXCLUSIVE ); //
                                         //タイマドライバーハンドル取得
    
    uint32_t divider = 39063;//周期レジスタ初期値//最初の割り込みまでの時間をセット
           //5 nsec x20 x 39063 x 256 = 1000.0128msec = 1sec  at Fblclk3 = 10MHz
    
    DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR );  //
                    //コールバック関数tmrISR( )の繰り返し呼び出しをセットアップ
    
    DRV_TMR_Start(myHandle);    //タイマ1割り込み許可、タイマ1イネーブル 
}

void APP_Tasks ( void )
{
    switch ( appData.state )
    { 
        case APP_STATE_INIT:
        {
            break;
        }
        case APP_STATE_SERVICE_TASKS:
        {
            break;
        }
        default:
        {
            break;
        }
    }
}
  
 system_init.c
....
....

const DRV_TMR_INIT drvTmr0InitData = //タイマの諸元のインスタンス生成と初期化
{
    .moduleInit.sys.powerState = DRV_TMR_POWER_STATE_IDX0,
    .tmrId = DRV_TMR_PERIPHERAL_ID_IDX0,
    .clockSource = DRV_TMR_CLOCK_SOURCE_IDX0, 
    .prescale = DRV_TMR_PRESCALE_IDX0,
    .mode = DRV_TMR_OPERATION_MODE_16_BIT,
    .interruptSource = DRV_TMR_INTERRUPT_SOURCE_IDX0,
    .asyncWriteEnable = false,
};

SYSTEM_OBJECTS sysObj;
....
....

void SYS_Initialize ( void* data )
{
....
....
    SYS_DEVCON_SystemUnlock();      //システム アンロック
    PB3DIVbits.PBDIV = 0x13;  //19: PBCLK3 = 1/20(デフォルトの1/10) 
    SYS_DEVCON_SystemLock(); //システムロック
....
....   
      sysObj.  drvTmr0 =     //戻り値:タイマ用システムモジュールのハンドル  
      DRV_TMR_Initialize(DRV_TMR_INDEX_0, (SYS_MODULE_INIT *)&drvTmr0InitData);
                //タイマ用システムモジュール初期化
....
....

    SYS_INT_Enable();

    APP_Initialize();
}
 
system_interrupt.c   
.....
.....
    
void __ISR(_TIMER_1_VECTOR, ipl1AUTO) IntHandlerDrvTmrInstance0(void)  //
{
    DRV_TMR_Tasks(sysObj.drvTmr0); //
}
  
 drv_tmr.c  
....
....
static unsigned int          gDrvTmrInstances = 0;  //現在の初期化済みインスタンス数 
....
....

typedef struct
{
    DRV_TMR_MODULE_INSTANCE         tmrInstance;     // the current timer module
    DRV_TMR_CLIENT_OBJ               tmrClients[1];   
               // Timer driver supports only Single Client. If you have multiple clients looking for timer, then use Timer System Service.
}DRV_TMR_MODULE_DESCRIPTOR;  //タイマモジュールデスクリプタ定義
....
....

SYS_MODULE_OBJ DRV_TMR_Initialize  //タイマ用システムモジュール初期化
( 
    const SYS_MODULE_INDEX drvIndex,
    const SYS_MODULE_INIT  * const init 
)
{
    
    DRV_TMR_MODULE_DESCRIPTOR* pTmrDcpt;//タイマ用システムモジュールの
                                        //デスクリプタのインスタンス生成
    DRV_TMR_MODULE_INSTANCE*   pTmrInst; //タイマ用システムモジュールの
                                         //インスタンス生成
    const DRV_TMR_INIT * tmrInit; //ダイナミックタイマの諸元(データ)
    
    
  //ドライバーインデックスが規定の範囲内にあるかチェック
    if ( drvIndex >= DRV_TMR_INSTANCES_NUMBER )
    {
        return SYS_MODULE_OBJ_INVALID;
    }

    //既に初期化されたハードウェアインスタンスがないかチェック
    pTmrDcpt = gDrvTmrDcpt + drvIndex; 
    if ( pTmrDcpt->tmrInstance.inUse != false )
    {
        return (SYS_MODULE_OBJ)pTmrDcpt;
    }

   
    tmrInit = ( const DRV_TMR_INIT * ) init;//取得アドレスをローカル変数にコピー
    ....
    ....

   // Object is valid, set it in use
    pTmrInst = &pTmrDcpt->tmrInstance;
    pTmrInst->inUse = true;

    //タイマモジュールのインスタンス初期化
    pTmrInst->tmrId = tmrInit->tmrId;//タイマID(タイマ1)設定
    pTmrInst->clockSource = tmrInit->clockSource; //(内部)クロック源設定
    pTmrInst->prescale = tmrInit->prescale;  //プリスケーラ(1/256)設定
    pTmrInst->interruptSource = tmrInit->interruptSource;//割り込みベクトル(INT_T1)設定
    pTmrInst->operMode = tmrInit->mode;//(16bit)動作モード設定
    ....
    ....   
    pTmrInst->status = SYS_STATUS_READY; //準備完了

    gDrvTmrInstances++;

       return (SYS_MODULE_OBJ)pTmrDcpt; //戻り値:符号なし整数...
       //タイマドライバーのハンドル(sysObj.drvTmr0)を返す
       //ハンドル(符号なし整数)=(SYS_MODULE_OBJ)(デスクリプタのポインタ)と定義 at Harmony
       //typedef uintptr_t SYS_MODULE_OBJ;
       //ポインタ型の整数型への変換は処理系定義
 
}

....
....

DRV_HANDLE DRV_TMR_Open //タイマドライバー初期化
( 
    const SYS_MODULE_INDEX index, 
    const DRV_IO_INTENT ioIntent 
)
{

    if (index >= DRV_TMR_INSTANCES_NUMBER || ( ioIntent & DRV_IO_INTENT_SHARED ) != 0)
    {   // only exclusive access for now.
        return DRV_HANDLE_INVALID;
    }

    if(gDrvTmrInstances != 0)//タイマ用システムモジュールが初期化されている場合
    {   // module initialized
        DRV_TMR_MODULE_DESCRIPTOR* pDcpt = gDrvTmrDcpt + index;
        if(pDcpt->tmrInstance.inUse != false)
        {   // search a free client
            int ix;

            DRV_TMR_CLIENT_OBJ* dObj = pDcpt->tmrClients + 0;
            for(ix = 0; ix < sizeof(pDcpt->tmrClients) / sizeof(*pDcpt->tmrClients); ix++, dObj++)
            {
                if(dObj->clientStatus == DRV_TMR_CLIENT_OBJ_CLOSED)
                {   // found free client
                    dObj->pModInst = &pDcpt->tmrInstance;
                    dObj->clientStatus = DRV_TMR_CLIENT_OBJ_READY;
                    return ( ( DRV_HANDLE ) dObj );//タイマドライバーの
                                                      //ハンドルを返す

                }
            }
        }
    }

    return DRV_HANDLE_INVALID;
}




bool DRV_TMR_AlarmRegister 
( 
    DRV_HANDLE handle, 
    uint32_t divider, 
    bool isPeriodic,
    uintptr_t context, 
    DRV_TMR_CALLBACK callBack 
)
{
    DRV_TMR_CLIENT_OBJ *dObj = _DRV_TMR_ClientObj(handle);

    if(dObj == 0)
    {
        return false;
    }

    DRV_TMR_OPERATION_MODE operMode = dObj->pModInst->operMode;
    bool success = false;

    if(operMode == DRV_TMR_OPERATION_MODE_32_BIT)
    {
        success = (divider >= DRV_TIMER_DIVIDER_MIN_32BIT && divider <= DRV_TIMER_DIVIDER_MAX_32BIT);
    }
    else
    {
        success = (divider >= DRV_TIMER_DIVIDER_MIN_16BIT && divider <= DRV_TIMER_DIVIDER_MAX_16BIT);
    }

    if(success)
    {
        bool resume = _DRV_TMR_Suspend(dObj);
        _DRV_TMR_ClientSetParams(dObj, divider - 1, isPeriodic, context, callBack);

        if(operMode == DRV_TMR_OPERATION_MODE_32_BIT)
        {
            PLIB_TMR_Period32BitSet ( dObj->pModInst->tmrId, divider - 1);
        }
        else
        {
            PLIB_TMR_Period16BitSet ( dObj->pModInst->tmrId, (uint16_t)divider - 1 );
        }
        if(resume)
        {
            _DRV_TMR_Resume(dObj, resume);
        }
        else
        {
            dObj->clientStatus = DRV_TMR_CLIENT_OBJ_ARMED;
        }
    }


    return success;
}

....
....

static void _DRV_TMR_ClientSetParams(
        DRV_TMR_CLIENT_OBJ* pObj, 
        uint32_t period, 
        bool isPeriodic,                        
        uintptr_t context, 
        DRV_TMR_CALLBACK callBack
)
{
    pObj->alarmCallback = callBack ;
    pObj->alarmPeriodic = isPeriodic;
    pObj->context = context;
    pObj->pModInst->timerPeriod = period;
    pObj->alarmCount = 0;
}

....
....


void DRV_TMR_Tasks ( SYS_MODULE_OBJ object ) //
{
    DRV_TMR_MODULE_DESCRIPTOR* pDcpt = _DRV_TMR_ModuleObj(object, true);

    if(pDcpt) //ハンドルが妥当な場合
    {
        DRV_TMR_ProcessEvents(pDcpt);
    }

}

....
....

static void DRV_TMR_ProcessEvents ( DRV_TMR_MODULE_DESCRIPTOR* pDcpt ) //
{
....
....
    DRV_TMR_CLIENT_OBJ* pClient = pDcpt->tmrClients;
....
....

     /* callback the application routine */
        if( pClient->alarmCallback != NULL )
        {
           (*pClient->alarmCallback)( pClient->context, pClient->alarmCount); //
                                         //コールバック関数の呼び出し
        }
....
....
} 


bool DRV_TMR_Start ( DRV_HANDLE handle )
{
    DRV_TMR_CLIENT_OBJ *dObj = _DRV_TMR_ClientObj(handle);

    if(dObj && dObj->clientStatus == DRV_TMR_CLIENT_OBJ_ARMED)
    {
        _DRV_TMR_Resume(dObj, true);

        /* Update the Client Status */
        dObj->clientStatus = DRV_TMR_CLIENT_OBJ_RUNNING;
        return true;
    }
    return false;
}


  
system_definitions.h   
.....
.....

typedef uintptr_t SYS_MODULE_OBJ; //システムモジュールのインスタンスへのハンドル
....
....


typedef struct
{
    SYS_MODULE_OBJ  sysDevcon; //システムのハンドル
    SYS_MODULE_OBJ  drvTmr0;   //タイマのハンドル

} SYSTEM_OBJECTS;

 system_config.h
.....
.....

//ダイナミックタイマの具体的設定値
#define DRV_TMR_INTERRUPT_MODE             true
#define DRV_TMR_INSTANCES_NUMBER           1
#define DRV_TMR_CLIENTS_NUMBER             1

//タイマ1の具体的ハードウェア諸元
#define DRV_TMR_PERIPHERAL_ID_IDX0          TMR_ID_1
#define DRV_TMR_INTERRUPT_SOURCE_IDX0       INT_SOURCE_TIMER_1
#define DRV_TMR_INTERRUPT_VECTOR_IDX0       INT_VECTOR_T1
#define DRV_TMR_ISR_VECTOR_IDX0             _TIMER_1_VECTOR
#define DRV_TMR_INTERRUPT_PRIORITY_IDX0     INT_PRIORITY_LEVEL1
#define DRV_TMR_INTERRUPT_SUB_PRIORITY_IDX0 INT_SUBPRIORITY_LEVEL0
#define DRV_TMR_CLOCK_SOURCE_IDX0           DRV_TMR_CLKSOURCE_INTERNAL
#define DRV_TMR_PRESCALE_IDX0               TMR_PRESCALE_VALUE_256
#define DRV_TMR_OPERATION_MODE_IDX0         DRV_TMR_OPERATION_MODE_16_BIT
#define DRV_TMR_ASYNC_WRITE_ENABLE_IDX0     false
#define DRV_TMR_POWER_STATE_IDX0            SYS_MODULE_POWER_RUN_FULL
....
....
   
 drv_tmr.h  
.....
.....
#define DRV_TMR_INDEX_0                        0
#define DRV_TMR_INDEX_1                             1
#define DRV_TMR_INDEX_2                             2
#define DRV_TMR_INDEX_3                             3
#define DRV_TMR_INDEX_4                             4
#define DRV_TMR_INDEX_5                             5
#define DRV_TMR_INDEX_6                             6
#define DRV_TMR_INDEX_7                             7
#define DRV_TMR_INDEX_8                             8       
#define DRV_TMR_INDEX_9                             9

....
....

//ダイナミックタイマの諸元を規定する構造体
typedef struct
{
    /* System module initialization. */
    SYS_MODULE_INIT moduleInit;

    /* Identifies timer hardware module (PLIB-level) ID */
    TMR_MODULE_ID tmrId;

    /* Clock Source select. */
    DRV_TMR_CLK_SOURCES clockSource;

    /* Prescaler Selection from the processor enumeration */
    TMR_PRESCALE prescale;
    
    /* Interrupt Source for TMR module.  If 'DRV_TMR_OPERATION_MODE_32_BIT' flag
       is selected the interrupt will be generated by the 2nd timer of the pair,
       the odd numbered one. */
    INT_SOURCE interruptSource;

    /* Select 16/32 bit operation mode.  32 bit mode will combine two 16 bit
       timer modules to form a 32 bit one.  This is usually only necessary for
       very long delays. */
    DRV_TMR_OPERATION_MODE mode;

    /* Asynchronous write enable configuration.  If true the asynchronous write
       is enabled.  For timers that do not support this feature the value is
       ignored */
    bool asyncWriteEnable;

} DRV_TMR_INIT;
....
....

typedef void ( *DRV_TMR_CALLBACK ) ( uintptr_t context, uint32_t alarmCount );//
  // DRV_TMR_CALLBACKは、voidを返す(戻り値のない)
  //第一引数uintptr_t context、第二引数uint32_t alarmCountの関数へのポインタ
....
....
  
 sys_module.h  
typedef uintptr_t SYS_MODULE_OBJ;   //システムモジュールインスタンスのハンドル
.....
.....

typedef union
{
    uint8_t         value;

    struct
    {
         uint8_t     powerState  : 4;
/*
    Predefined powerState Values:

    - 0.  SYS_MODULE_POWER_OFF        - Module power-state power off state code
    - 1.  SYS_MODULE_POWER_SLEEP      - Module power-state sleep state code
    - 2.  SYS_MODULE_POWER_IDLE_STOP  - Module power-state idle-stop state code
    - 3.  SYS_MODULE_POWER_IDLE_RUN   - Module power-state idle-run state code
    - 4.  through 14.                 - Module-specific meaning
    - 15. SYS_MODULE_POWER_RUN_FULL   - Module power-state run-full state code
*/

        uint8_t     reserved    : 4;
    }sys;

} SYS_MODULE_INIT;
  
 drv_tmr_local.h  
.....
.....

typedef struct
{
    /* The status of the driver */
    SYS_STATUS                                              status;

    /* Time Period Value */
    uint32_t                                                timerPeriod;

    /* Pre-scaler Selection from the processor enumeration */
    TMR_PRESCALE                                            prescale;

    /* Timer Peripheral Id */
    TMR_MODULE_ID                                           tmrId;

        /* Timer Peripheral Id associated with Timer's 32-bi mode */
    // TMR_MODULE_ID                                            tmrNextId;

    /* Timer clock source */
    DRV_TMR_CLK_SOURCES                                     clockSource;

    /* Interrupt Source for TMR Interrupt */
    INT_SOURCE                                              interruptSource;

    /* TMR Driver HW instance operational flags */
    bool                                                    inUse;

        /* operation mode */
        DRV_TMR_OPERATION_MODE                                  operMode;

    /* Asynchronous write enable, if supported */
    bool                                                    asyncWriteEnable;       

    /* Semaphore to protect 'alarm count' */
    OSAL_SEM_HANDLE_TYPE                                    alarmCountSemaphore;

} DRV_TMR_MODULE_INSTANCE;
....
....

typedef struct
{
    /* Modul einstance this client belongs to */
    DRV_TMR_MODULE_INSTANCE*                    pModInst;

        /* Do the alarm periodically */
        bool                                        alarmPeriodic;
        
    /* Status of the client object */
    DRV_TMR_CLIENT_OBJ_STATUS                   clientStatus;

    /* Client's alarm counter */
    uint32_t                                    alarmCount;

    /* Alarm callback */
    DRV_TMR_CALLBACK                            alarmCallback;
                                         //コールバック関数へのポインタ

    /* Client context */
    uintptr_t                                   context;


} DRV_TMR_CLIENT_OBJ;
  





■ プロジェクト作成手順(ダイナミックタイマ割り込みを使った場合)
   はじめてHarmonyの プロジェクトをつくる方も対象に、プロジェクト作成要領について説明します。
   以下は、Harmony v1.08、MPLABX v.3.35 をもとに作成してあります。 (10)のPin Settinsのポート設定の画面がHarmony v.2以降大幅に
  変わりましたのでポート設定画面は v2.04のものに変更してあります。 Harmony v.2.04の時点では、他の部分の変更はありません。
  (最新の Harmony や MPLABX とは、若干異なるところがある可能性がありますので注意してください。)
  (1) Harmony プロジェクト選択
    MPLABXのメニューバーで[File]→[New Project]とすると 下図の[New Project]ダイアログが開きます。[Categories]の[Microchip Embedded]を選んだあと [Projects]の[32bit−MPLAB Harmony Project]を選択して[Next]をクリックします。
     
  (2)  プロジェクトフォルダの作成
  C:\microchip\harmony\v1_08_01\appsフォルダに下にプロジェクトフォルダをつくります。 これ以外のところにプロジェクトフォルダをつくるとコンパイルできなくなることがあります。 今回だけでなく後々他にもたくさんHarmonyのプロジェクトをつくことになるのでここではC:\microchip\harmony\v1_08\apps\_myWork\Test\Basicフォルダの下にプロジェクトフォルダをつくります。
     
  (3)  プロジェクト名、デバイス名 
    ここではプロジェクト名を"03 Led FreeRun Dynamic tmrINT"として、Project Nameに記入します。使用するデバイス名としてPIC32MZ2048EFH100を選択して [Finish]をクリックします。
     
   (4) プロジェクト画面の表示 
    プロジェクト全体の画面があらわれます。 
   
  (5)  プロジェクトのプロパティ設定
    プロジェクトを右クリックしてドロップダウンリストの中から[Properties]を選択します。
   
  (6)  コンパイラの設定
    Project Propertiesのダイアログが開きます。特に理由がなければXC32の最新のコンパイラを選択します。
     
  (7)  シフトJIS選択 
    Categoriesボックスの[General]を選択して、 Encodiongのコンボボックスから[Shift-JIS]を選択します。
   
  (8)   MHCの表示
    右のボックスにMHC(MPLAB Harmony Configurator)の画面が表示されていない場合は、 メニューバーから[Tools] → [Embedded] → [MPLAB Harmony Configurator]をクリックするとMHCの画面が現れます。 
 尚、MHC画面を表示するには左側のプロジェクトWinodowでMHCを開きたいプロジェクトをドロップダウンリストで main project に設定しておく必要があります。
   
  (9)  クロックの設定
    [Clock Diagram]のタブをクリックしてクロック設定の画面を開きます。
 PIC32MZのOSC1ピンに 24MHzの外付け水晶発振器を取り付け、システムクロックが200MHzとなるクロックモジュールの設定を行います。プリプロセッサコマンドで記述すると以下のようになります。

 #pragma config FNOSC = SPLL
 #pragma config POSCMOD = EC
 #pragma config FPLLICLK = PLL_POSC
 #pragma config FPLLIDIV = DIV_3
 #pragma config FPLLMULT = MUL_50
 #pragma config FPLLODIV = DIV_2

 この設定を実現すべくクロックモジュール設定画面で  FNOSC、 POSCMOD、 FPLLICLK、 FPLLIDIV、FPLLMULT、FPLLODIVの各項目のコンボボックスからそれぞれ SPLL、 EC、 PLL_POSC、 DIV_3、 MUL_50、 DIV_2を選択します。
 (下記画面は 画像をクリックすると画像が拡大します)
     
     <追記>
 クロックの設定は以下の MHCのDevice & Project Configuration画面でも設定できます。

     
  (10)   ポートの設定
    ポートの設定(下記のPin Settingsの画面は Harmony v2.04のものです)
[Pin Settings]のタブを開きます。 Pinの RG15で Function欄を GPIO_OUT に変更します
   
  (11)  ダイナミックタイマ選択(Dynamic Timer Drivers Librayを選択 )
    MHCの[Option]タブを選択して、ツリーからHarmony Framework Configuration→Driver→Timerを選択します。そして"Use Timer Driver ?"のチェックに入れ、Driver Implementationで "DYNAMIC"を選択します。 更に、"Interrupt Mode"、"TMR Driver Instance 0" のチェックをいれます。後は、デフォルト(タイマ1選択、割り込み優先度1、内部タイマクロック、プリスケーラ1/256など)のままにしておきます。
 
     
  (12)  プロジェクト コードの生成
    (Generate Code)ボタンをクリックして、*.hや*.cなどのプロジェクトに関連するファイルを一括で作成します。 MHCを保存するかどうかきいてきますので、特に理由がない場合は SaveボタンをクリックしてMHCを保存します。
   

Generateボタンをクリックしてプロジェクトファイルを一式生成します。

  (13)  変数の宣言 
     プロジェクトファイルが一式できあがりました。クリーンビルドしてここまで問題なくプロジェクトファイルができあがっていることを確認します。

 アプリケーションのコーディングの最初として、app.cの中にタイマ1のハンドルとLEDの状態フラグの変数を宣言します。

DRV_HANDLE myHandle;
bool LED;

     
  (14)  コールバック関数の定義
    次に コールバック関数を定義します。 コールバック関数には、呼ばれた場合次の処理をする実行分を書き込みます。
 ・LEDが消灯していたら点灯し、点灯時間を周期レジスタに書き込む
 ・LEDが点灯していたら消灯し、消灯時間を周期レジスタに書き込む 
 
void tmrISR(uintptr_t context, uint32_t alarmCount ) //タイマ1コールバック関数 { if(LED == false) { LED = true; PLIB_PORTS_PinSet( PORTS_ID_0, PORT_CHANNEL_G, 15 ); //RG15: LED点灯     //LATGbits.LATG15 = 1; } } else { LED = false; PLIB_PORTS_PinClear( PORTS_ID_0, PORT_CHANNEL_G, 15 ); //RG15: LED消灯     //LATGbits.LATG15 = 0; } } };


  (15)  ハンドル取得、割り込み許可
    app.cの中の APP_Initialize( )関数の中で
 @タイマ1のドライバーをオープンして、戻り値としてハンドルを取得します。 
 A最初のタイマ1の割り込み発生までの時間を周期レジスタに設定します。
 B繰り返しコールバック関数を呼び出すようにセットアップします。
 Cタイマ1の割り込みを許可し、タイマ1をイネーブルにします。          
void APP_Initialize ( void )
{
   ....
   ....
    
    myHandle = DRV_TMR_Open ( DRV_TMR_INDEX_0, DRV_IO_INTENT_EXCLUSIVE );
                                                            //タイマ1ハンドル取得
    
    uint32_t divider = 39063;   //周期レジスタ初期値    //最初の割り込みまでの時間をセット
                    //5 nsec x20 x 39063 x 256 = 1000.0128msec = 1sec  at Fblclk3 = 10MHz
    
    DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR ); //繰り返し呼び出し
                                //コールバック関数繰り返し呼び出しをセットアップ
                 //    bool DRV_TMR_AlarmRegister(
                                //                              DRV_HANDLE handle, 
                                //                              uint32_t divider, //周期レジスタ初期値
                                //                              bool isPeriodic,  //周期性の有無  
                                //                              uintptr_t context, 
                                //                              DRV_TMR_CALLBACK callBack
                                //                              );


    DRV_TMR_Start(myHandle);    //タイマ1割り込み許可、タイマ1イネーブル 
}


 
  (16)   タイマ入力周波数変更
   
 デフォルトでは、タイマの入力クロックは100MHzです。従ってプリスケール1/256の16ビットタイマの最大カウント時間は
167.7696 msec( = 10nsec x 256 x 65535 = 167769600 nsec)です。 所要の1secより短いのでタイマの入力クロックの
ペリフェラルクロック(PBCLK3)を10MHzに変更します。PB3DIVレジスタを変更することによってペリフェラルクロック
(PBCLK3)の周波数を変えることができます。 PBCLK3レジスタを変更するには、事前にシステムロックを実施し、
変更後にシステムロックを解除する必要があります

    SYS_DEVCON_SystemUnlock();
    PB3DIVbits.PBDIV = 0x13;
    SYS_DEVCON_SystemLock(); 

  (17)  プロジェクトのクリーンビルド
    プロジェクトのクリーンとビルドを行います。
   
   
実行ファイルをデバイスに書き込みます。